package vip.zhenzicheng.websocket.websocket.handler;

import com.alibaba.fastjson.JSON;
import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.lang.NonNull;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.PongMessage;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import vip.zhenzicheng.websocket.websocket.config.interceptor.TokenInterceptor;

import javax.websocket.OnOpen;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 文本Handler方式处理消息，如果想实现在握手阶段解token的动作，可以配合使用{@linkplain TokenInterceptor 拦截器}
 *
 * @author zichengzhen
 * @date 2023-03-20 11:49
 * @see org.springframework.web.socket.WebSocketHandler
 * @see TokenInterceptor
 */
@Slf4j
public class WebSocketConnection extends TextWebSocketHandler {

    /**
     * 连接缓冲池,单体服务内存级缓存,不能跨进程共享
     * 可以做在线人数统计等功能，不过这里没有使用统一用户的概念，所以无法获取用户登录的唯一标识
     */
    @Setter
    @Getter
    private static Map<String, WebSocketSession> sessionPools = new ConcurrentHashMap<>();

    /**
     * 定时任务是否已经启动过
     */
    private volatile boolean taskStart = false;

    // @Autowired
    // private TokenStore tokenStore;


    /**
     * 建立连接后
     *
     * @see OnOpen
     */
    @Override
    public void afterConnectionEstablished(@NonNull WebSocketSession webSocketSession) throws Exception {
        log.info("连接已建立");
        sendMessage(webSocketSession, "连接已建立");
        String userCode = getUserCodeByToken((String) webSocketSession.getAttributes().get("token"));
        if (StringUtils.hasLength(userCode)) {
            // 跳过token
        }
        // TODO 缓存当前userCode与session对应关系，无效userCode
        sessionPools.put(webSocketSession.getId(), webSocketSession);
        // 所有的检查消息都是在sendMessage中发送的,并不在连接时发送
    }

    /**
     * 从当前WebSocketSession中解析token,并获取userCode
     */
    private String getUserCodeByToken(String token) {
        // 根据token获取accessToken
        // OAuth2AccessToken accessToken = tokenStore.readAccessToken(token);
        // Assert.isTrue(accessToken != null && StringUtils.hasLength(accessToken.getValue()), "token解析错误");
        // OAuth2Authentication oAuth2Authentication = tokenStore.readAuthentication(accessToken);
        // String parseTokenError = "解析token异常";
        // Assert.notNull(oAuth2Authentication, parseTokenError);
        // Assert.notNull(oAuth2Authentication.getUserAuthentication(), parseTokenError);
        // Assert.notNull(oAuth2Authentication.getUserAuthentication().getPrincipal(), parseTokenError);
        // log.info("token解析成功");
        // PigUser pigUser = (PigUser) oAuth2Authentication.getUserAuthentication().getPrincipal();
        // return pigUser.getUserCode();
        return "";
    }

    public void sendMessage(String userCode, Object message) {
        if (!StringUtils.hasLength(userCode)) {
            return;
        }
        sendMessage(sessionPools.get(userCode), message);
    }

    /**
     * 给客户端发送消息
     *
     * @param webSocketSession
     * @param message          发送的消息,如果不是String类型则会转换为JSON字符串
     */
    private void sendMessage(WebSocketSession webSocketSession, Object message) {
        Assert.notNull(webSocketSession, "webSocketSession is null!");
        if (!webSocketSession.isOpen()) {
            log.warn("session已关闭,取消发送消息");
            return;
        }
        try {
            String jsonMsg = message instanceof String ? (String) message : JSON.toJSONString(message);
            webSocketSession.sendMessage(new TextMessage(jsonMsg));
        } catch (IOException e) {
            e.printStackTrace();
            log.error(e.getMessage());
        }
    }

    /**
     * 接受客户端消息
     *
     * @see javax.websocket.OnMessage
     */
    @Override
    public void handleTextMessage(WebSocketSession webSocketSession, TextMessage message) throws Exception {
        log.info("收到客户端消息:{}", message.getPayload());
        sendMessage(webSocketSession, message.getPayload());
    }

    @Override
    protected void handlePongMessage(WebSocketSession session, PongMessage message) throws Exception {
        log.info("xxx");
    }

    /**
     * 连接关闭后,清除连接池中的数据
     *
     * @see javax.websocket.OnClose
     */
    @SneakyThrows
    @Override
    public void afterConnectionClosed(@NonNull WebSocketSession webSocketSession, @NonNull CloseStatus status) {
        // String userCode = (String) webSocketSession.getAttributes().get("token");
        // WebSocketSession existSession = sessionPools.get(null);
        // if (existSession != null) {
        //     sendMessage(existSession, "连接关闭");
        //     existSession.close();
        // sessionPools.remove(userCode, existSession);
        // }
        log.info("连接已断开");
    }

    /**
     * 出现异常
     *
     * @see javax.websocket.OnError
     */
    @Override
    public void handleTransportError(@NonNull WebSocketSession webSocketSession, Throwable exception) throws Exception {
        exception.printStackTrace();
        log.error("发生错误:{}", exception.getMessage());
    }
}